%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, <COMPANY>
%%% @doc
%%% hprose 序列化
%%% @end
%%% Created : 11. 五月 2018 11:22
%%%-------------------------------------------------------------------
-module(hprose_reader).
-include("hprose.hrl").

%% API
-export([deserialize/1]).

-spec deserialize(hprose_protocol()) -> list().
deserialize(V) -> deserialize(V, []).

deserialize(<<Integer, Stream/binary>>, Result) when Integer >= $0, Integer =< $9 ->
  deserialize(Stream, [binary_to_integer(<<Integer>>) | Result]);

deserialize(<<?TAG_INTEGER, Stream/binary>>, Result) ->
  [Value, TStream] = binary:split(Stream, <<?TAG_SEMICOLON>>),
  deserialize(TStream, [binary_to_integer(Value) | Result]);

deserialize(<<?TAG_LONG, Stream/binary>>, Result) ->
  [Value, TStream] = binary:split(Stream, <<?TAG_SEMICOLON>>),
  deserialize(TStream, [binary_to_integer(Value) | Result]);

deserialize(<<?TAG_DOUBLE, Stream/binary>>, Result) ->
  [Value, TStream] = binary:split(Stream, <<?TAG_SEMICOLON>>),
  deserialize(TStream, [binary_to_float(Value) | Result]);

deserialize(<<?TAG_NAN, Stream/binary>>, Result) ->
  deserialize(Stream, [nan | Result]);

deserialize(<<?TAG_INFINITY, ?TAG_POS, Stream/binary>>, Result) ->
  deserialize(Stream, [positive_infinity | Result]);

deserialize(<<?TAG_INFINITY, ?TAG_NEG, Stream/binary>>, Result) ->
  deserialize(Stream, [negative_infinity | Result]);

deserialize(<<?TAG_TRUE, Stream/binary>>, Result) ->
  deserialize(Stream, [true | Result]);

deserialize(<<?TAG_FALSE, Stream/binary>>, Result) ->
  deserialize(Stream, [false | Result]);

deserialize(<<?TAG_EMPTY, Stream/binary>>, Result) ->
  deserialize(Stream, [empty | Result]);

deserialize(<<?TAG_STRING, Stream/binary>>, Result) ->
  [_Value, TStream] = binary:split(Stream, <<?TAG_QUOTE>>),
  [<<Value/binary>>, TStream1] = binary:split(TStream, <<?TAG_QUOTE>>),
  deserialize(TStream1, [{string, unicode:characters_to_list(Value)} | Result]);

deserialize(<<?TAG_UTF8CHAR, Value/utf8, Stream/binary>>, Result) ->
  deserialize(Stream, [{char, Value} | Result]);

deserialize(<<?TAG_BYTES, Stream/binary>>, Result) ->
  [_Value, TStream] = binary:split(Stream, <<?TAG_QUOTE>>),
  [Value, TStream1] = binary:split(TStream, <<?TAG_QUOTE>>),
  deserialize(TStream1, [Value | Result]);

deserialize(<<?TAG_GUID, ?TAG_OPENBRACE, Stream/binary>>, Result) ->
  [<<UUID:36/binary>>, TStream] = binary:split(Stream, <<?TAG_CLOSEBRACE>>),
  deserialize(TStream, [{guid, UUID} | Result]);

deserialize(<<?TAG_DATE, Stream/binary>>, Result) ->
  {Date, TStream} = read_date(Stream),
  deserialize(TStream, [Date | Result]);

deserialize(<<?TAG_TIME, Stream/binary>>, Result) ->
  {Date, TStream} = read_time(Stream),
  deserialize(TStream, [Date | Result]);

deserialize(<<?TAG_NULL, Stream/binary>>, Result) ->
  deserialize(Stream, [null | Result]);

deserialize(<<?TAG_LIST, TStream/binary>>, Result) ->
  {List, TStream1} = read_list(TStream),
  deserialize(TStream1, [List | Result]);

deserialize(<<?TAG_MAP, Stream/binary>>, Result) ->
  {List, TStream} = read_map(Stream),
  deserialize(TStream, [List | Result]);

deserialize(<<?TAG_CLASS, Stream/binary>>, Result) ->
  {List, TStream} = read_class(Stream),
  deserialize(TStream, [List | Result]);

deserialize(<<?TAG_END, _Stream/binary>>, Result) ->
  lists:reverse(Result);

deserialize(<<>>, Result) -> lists:reverse(Result);

deserialize(Stream, Result) ->
  io:format("deserialize_error:~w~n", [{Stream, lists:reverse(Result)}]).

%% 解析一个本地日期
-spec read_date(hprose_protocol()) -> {date(), hprose_protocol()}.
read_date(<<Value:8/binary, ?TAG_SEMICOLON, Stream/binary>>) ->
  Date = binary_to_integer(Value),
  Year = Date div 10000,
  Month = Date div 10000 rem 100,
  Day = Date div 100,
  {{{Year, Month, Day}, {0, 0, 0, 0}}, Stream};

%% 解析一个UTC日期
read_date(<<Value:8/binary, ?TAG_UTC, Stream/binary>>) ->
  Date = binary_to_integer(Value),
  Year = Date div 10000,
  Month = Date div 10000 rem 100,
  Day = Date div 100,
  {{utc, {Year, Month, Day}, {0, 0, 0, 0}}, Stream};

%% 解析一个日期时间
read_date(Stream) -> read_datetime(Stream).

%% 解析一个时间
-spec read_time(hprose_protocol()) -> {time(), hprose_protocol()}.
read_time(<<Value:6/binary, ?TAG_SEMICOLON, Stream/binary>>) ->
  Time = binary_to_integer(Value),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  {{{0, 0, 0}, {Hour, Minute, Seconds, 0}}, Stream};

%% 解析一个UTC时间
read_time(<<Value:6/binary, ?TAG_UTC, Stream/binary>>) ->
  Time = binary_to_integer(Value),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  {{utc, {0, 0, 0}, {Hour, Minute, Seconds, 0}}, Stream};

%% 解析一个时间带毫秒
read_time(<<Value:6/binary, ?TAG_POINT, Stream/binary>>) ->
  Time = binary_to_integer(Value),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  case read_millisecond(Stream) of
    {Millisecond, TStream} ->
      {{{0, 0, 0}, {Hour, Minute, Seconds, Millisecond}}, TStream};
    {utc, Millisecond, TStream} ->
      {{utc, {0, 0, 0}, {Hour, Minute, Seconds, Millisecond}}, TStream}
  end.

%% 解析一个日期时间
-spec read_datetime(hprose_protocol()) -> {{date(), time()}, hprose_protocol()}.
read_datetime(<<Value1:8/binary, ?TAG_TIME, Value2:6/binary, ?TAG_SEMICOLON, Stream/binary>>) ->
  Date = binary_to_integer(Value1),
  Year = Date div 10000,
  Month = Date div 10000 rem 100,
  Day = Date div 100,
  Time = binary_to_integer(Value2),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  {{{Year, Month, Day}, {Hour, Minute, Seconds, 0}}, Stream};

%% 解析一个UTC日期时间
read_datetime(<<Value1:8/binary, ?TAG_TIME, Value2:6/binary, ?TAG_UTC, Stream/binary>>) ->
  Date = binary_to_integer(Value1),
  Year = Date div 10000,
  Month = Date div 10000 rem 100,
  Day = Date div 100,
  Time = binary_to_integer(Value2),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  {{utc, {Year, Month, Day}, {Hour, Minute, Seconds, 0}}, Stream};

%% 解析一个日期时间带毫秒
read_datetime(<<Value1:8/binary, ?TAG_TIME, Value2:6/binary, ?TAG_POINT, Stream/binary>>) ->
  Date = binary_to_integer(Value1),
  Year = Date div 10000,
  Month = Date div 10000 rem 100,
  Day = Date div 100,
  Time = binary_to_integer(Value2),
  Hour = Time div 10000,
  Minute = Time div 100 rem 100,
  Seconds = Time rem 100,
  case read_millisecond(Stream) of
    {Millisecond, TStream} ->
      {{{Year, Month, Day}, {Hour, Minute, Seconds, Millisecond}}, TStream};
    {utc, Millisecond, TStream} ->
      {{utc, {Year, Month, Day}, {Hour, Minute, Seconds, Millisecond}}, TStream}
  end.

%% 解析一个时间的毫秒
-spec read_millisecond(hprose_protocol()) -> non_neg_integer().
read_millisecond(Stream) -> read_millisecond(1, Stream).
read_millisecond(SubLen, Stream) ->
  case Stream of
    <<Millisecond:SubLen/binary, ?TAG_SEMICOLON, Stream/binary>> -> {binary_to_integer(Millisecond), Stream};
    <<Millisecond:SubLen/binary, ?TAG_UTC, Stream/binary>> -> {utc, binary_to_integer(Millisecond), Stream};
    _ -> read_millisecond(SubLen + 1, Stream)
  end.


%% 截取一个结构
-spec sub_structure(hprose_protocol()) -> {hprose_protocol(), hprose_protocol()}.
sub_structure(<<?TAG_OPENBRACE, ?TAG_CLOSEBRACE, Stream/binary>>) ->
  {<<>>, Stream};
sub_structure(Stream) ->
  [_, TStream] = binary:split(Stream, <<?TAG_OPENBRACE>>),
  sub_structure(1, TStream, 1).
sub_structure(SubLen, Stream, BalanceValue) ->
  case Stream of
    <<_SubValue:SubLen/binary, ?TAG_OPENBRACE, _TStream/binary>> ->
      sub_structure(SubLen + 1, Stream, BalanceValue + 1);
    <<SubValue:SubLen/binary, ?TAG_CLOSEBRACE, TStream/binary>> ->
      Value1 = BalanceValue - 1,
      case Value1 of
        0 -> {SubValue, TStream};
        _ -> sub_structure(SubLen + 1, Stream, Value1)
      end;
    _ -> sub_structure(SubLen + 1, Stream, BalanceValue)
  end.

%% 解析一个list
-spec read_list(hprose_protocol()) -> {list(), hprose_protocol()}.
read_list(Stream) ->
  {SubValue, TStream} = sub_structure(Stream),
  {deserialize(SubValue), TStream}.

%% 解析一个map
-spec read_map(hprose_protocol()) -> {list(), hprose_protocol()}.
read_map(Stream) ->
  {SubValue, TStream} = sub_structure(Stream),
  {read_map(deserialize(SubValue), #{}), TStream}.
read_map([Key, Value | T], Result) ->
  read_map(T, Result#{Key => Value});
read_map([], Result) -> Result.

%% 解析一个类
-spec read_class(hprose_protocol()) -> {map(), hprose_protocol()}.
read_class(Stream) ->
  [Value, TStream] = binary:split(Stream, <<?TAG_QUOTE>>),
  Len = binary_to_integer(Value),
  <<ClassName:Len/binary, ?TAG_QUOTE, TStream1/binary>> = TStream,
  %% 截取字段
  {SubValue1, TStream2} = sub_structure(TStream1),
  Fields = deserialize(SubValue1),
  <<?TAG_OBJECT, TStream3/binary>> = TStream2,
  %% 截取值
  {SubValue2, TStream4} = sub_structure(TStream3),
  Values = deserialize(SubValue2),
  {read_class(#{class_name =>ClassName}, lists:zip(Fields, Values)), TStream4}.
read_class(Class, [{Field, Value} | T]) ->
  Class1 = Class#{binary_to_atom(Field, utf8)=>Value},
  read_class(Class1, T);
read_class(Class, []) -> Class.
